/*******************************************************************************
 * 创建: 2025年09月28日
 * 作者: 水煮肉片饭(27185709@qq.com)
 * 描述: 多语言模块
*******************************************************************************/
import { _decorator, Component, Node, Asset, AssetManager, assetManager, director, UIRenderer, js, __private, JsonAsset, Label, Sprite, SpriteFrame, sp, Canvas, AudioClip } from 'cc';
const { ccclass, disallowMultiple, property } = _decorator;
@ccclass
@disallowMultiple
class Language extends Component {
    @property
    key: string = '';
}
let _lang: string = 'En';
let _langAssets: Object = js.createMap(true);
function _langLoad<T extends Asset>(path: string, type: __private.__types_globals__Constructor<T>): Promise<T> {
    return new Promise<T>((resolve) => {
        if (!path) { resolve(null); return; }
        let assets = _langAssets[_lang] ??= js.createMap(true);
        let asset = assets[path];
        if (asset) { resolve(asset); return; }
        ((callback: (bundle: AssetManager.Bundle) => void) => {
            let bundle = assetManager.getBundle(_lang);
            if (bundle) { callback(bundle); return; }
            assetManager.loadBundle(_lang, (err: Error, bundle: AssetManager.Bundle) => { callback(err ? null : bundle); })
        })((bundle: AssetManager.Bundle): void => {
            if (!bundle) { resolve(null); return; }
            bundle.load(path, type, (err: Error, asset: T) => {
                if (err) {
                    if (type.name !== 'SpriteFrame') { resolve(null); return; }
                    bundle.load(`${path}/spriteFrame`, type, (err: Error, asset: T) => {
                        resolve(err ? null : assets[path] = asset);
                    });
                } else {
                    resolve(assets[path] = asset);
                }
            });
        });
    });
}
Object.assign((globalThis as any).gi ||= {}, {
    langName: function (): string { return _lang },
    langLoad: function (lang: string): Promise<void> {
        return new Promise<void>(async (resolve) => {
            _lang = lang;
            await gi.langTranslate(director.getScene().getComponentInChildren(Canvas).node);
            resolve();
        });
    },
    langRelease: function (language?: string): void {
        if (language === undefined) { for (let lang in _langAssets) { gi.langRelease(lang); } return; }
        let assets = _langAssets[language];
        for (let key in assets) { assetManager.releaseAsset(assets[key]); }
        delete _langAssets[language];
        let bundle = assetManager.getBundle(language);
        bundle && assetManager.removeBundle(bundle);
    },
    langTranslate: function (node: Node): Promise<void> {
        return new Promise<void>(async (resolve) => {
            if (!node) { console.error('gi.langTranslate翻译失败！'); resolve(); return; }
            let json = (await _langLoad(_lang, JsonAsset))?.json;
            if (!json) { console.error(`gi.langTranslate翻译失败！节点"${node.name}"，语种"${_lang}"`); resolve(); return; }
            let labelData = json.Label ?? {};
            let spriteData = json.Sprite ?? {};
            let spineData = json.Spine ?? {};
            let audioData = json.Audio ?? {};
            let trans = async (node: Node) => {
                let comp: any = node.getComponent(UIRenderer);
                if (comp) {
                    let key = node.getComponent(Language)?.key || node.name;
                    switch (comp.constructor) {
                        case Label:
                            let lbData = labelData[key];
                            lbData !== undefined && (comp.string = lbData);
                            break;
                        case Sprite:
                            let sptFrame = await _langLoad(spriteData[key], SpriteFrame);
                            sptFrame && (comp.spriteFrame = sptFrame);
                            break;
                        case sp.Skeleton:
                            let spData = await _langLoad(spineData[key], sp.SkeletonData);
                            spData && (comp.skeletonData = spData);
                            break;
                    }
                }
                for (let children = node.children, i = children.length - 1; i >= 0; await trans(children[i--]));
            }
            await trans(node);
            for (let key in audioData) { await _langLoad(audioData[key], AudioClip); }
            resolve();
        });
    },
    langValue: function (type: 'Label' | 'Sprite' | 'Spine' | 'Audio', key: string): string {
        let val = _langAssets[_lang]?.[_lang]?.json?.[type]?.[key];
        if (!val) { console.error(`gi.langValue获取语种数据失败！${_lang}/${type}/${key}`); return ''; }
        return val;
    },
    langAsset: function (type: 'Sprite' | 'Spine' | 'Audio', key: string): any {
        let val = _langAssets[_lang]?.[_lang]?.json?.[type]?.[key];
        if (!val) { console.error(`gi.langAsset获取语种资源失败！${_lang}/${type}/${key}`); return null; }
        return _langAssets[_lang][val];
    },
    langFormat: function (label: Label, mark: string, ...replace: (string | number)[]): void {
        for (var str = '', arr = label.string.split(mark), i = 0, len = arr.length; i < len; i++) {
            str += arr[i] + (replace[i] === undefined || i === len - 1 ? '' : replace[i].toString());
        }
        label.string = str;
    },
});
declare global {
    module gi {
        /** 获取当前语种名称 */
        function langName(): string;
        /** 切换语种，并将所有多语言Label、Sprite、Spine、Audio切换为对应语种bundle中的资源 */
        function langLoad(language: string): Promise<void>;
        /** 释放语种language的bundle资源，如果language缺省则释放所有语种的bundle资源 */
        function langRelease(language?: string): void;
        /** 翻译node及其所有子节点中的多语言Label、Sprite、Spine、Audio，只有langLoad之后添加的节点才需要调用该方法 */
        function langTranslate(node: Node): Promise<void>;
        /** 获取当前语种json中key对应的值，type多语言组件类型 */
        function langValue(type: 'Label' | 'Sprite' | 'Spine' | 'Audio', key: string): string;
        /** 获取当前语种json中key对应的资源，type多语言组件类型 */
        function langAsset(type: 'Sprite' | 'Spine' | 'Audio', key: string): any;
        /** 将label的文本中所有mark字符串依次替换为replace数组中对应索引的元素 */
        function langFormat(label: Label, mark: string, ...replace: (string | number)[]): void;
    }
}